﻿using System;
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Assetbundles
{
    public abstract class AssetbundlebaseLoadOperation : IEnumerator
    {
        public object Current
        {
            get
            {
                return null;
            }
        }

        public bool MoveNext()
        {
            return !IsDone();
        }

        public void Reset()
        {

        }

        public abstract bool IsDone();

        public abstract bool Update();
    }


    #region Download

    public abstract class AssetbundleDownloadOperation : AssetbundlebaseLoadOperation
    {
        public string bundleName { get; private set; }
        public LoadedAssetbundle loadAssetbundle { get; protected set; }
        public string error { get; protected set; }

        protected abstract bool downloadIsDone { get; }
        protected abstract void FinishDownload();

        private bool firstDone = true;

        public override bool Update()
        {
            if (firstDone && downloadIsDone)
            {
                FinishDownload();
                //ensure FinishDownload be called only one time
                firstDone = false;
            }
            return !downloadIsDone;
        }

        public override bool IsDone()
        {
            return downloadIsDone;
        }

        public abstract string GetSourceURL();

        public AssetbundleDownloadOperation(string assetbundleName)
        {
            bundleName = assetbundleName;
        }
    }

    public class AssetBundleWWWDownloadOperation : AssetbundleDownloadOperation
    {
        private WWW _www;
        private string _url;

        public AssetBundleWWWDownloadOperation(string assetbundlename, WWW www)
         : base(assetbundlename)
        {
            if (null == www)
            {
                throw new ArgumentNullException("WWW");
            }
            _www = www;
            _url = www.url;
        }


        protected override bool downloadIsDone
        {
            get
            {
                return null == _www || _www.isDone;
            }
        }

        protected override void FinishDownload()
        {
            error = _www.error;
            if (!string.IsNullOrEmpty(error))
            {
                return;
            }

            AssetBundle bundle = _www.assetBundle;
            if (null == bundle)
            {
                error = string.Format("{0} is not a valid asset bundle.", bundleName);
            }
            else
            {
                loadAssetbundle = new LoadedAssetbundle(bundle);
            }

            _www.Dispose();
            _www = null;
        }

        public override string GetSourceURL()
        {
            return _url;
        }
    }

    #endregion


    #region LoadLevel

#if UNITY_EDITOR
    public class AssetbundleLoadLevelSimulateOperation : AssetbundlebaseLoadOperation
    {
        private AsyncOperation _operation = null;

        public AssetbundleLoadLevelSimulateOperation(string assetbundleName, string levelName, bool isAdditive = false)
        {
            string[] levelpaths = UnityEditor.AssetDatabase.GetAssetPathsFromAssetBundleAndAssetName(assetbundleName, levelName);
            if (0 == levelpaths.Length)
            {
                Debug.LogError("There is no scene with name \"" + levelName + "\" in " + assetbundleName);
                return;
            }

            if (isAdditive)
                _operation = UnityEditor.EditorApplication.LoadLevelAdditiveAsyncInPlayMode(levelpaths[0]);
            else
                _operation = UnityEditor.EditorApplication.LoadLevelAsyncInPlayMode(levelpaths[0]);
        }

        public override bool IsDone()
        {
            return null == _operation || _operation.isDone;
        }

        public override bool Update()
        {
            return false;
            //the process is very fast
        }
    }
#endif

    public class AssetBundleLoadLevelOperation : AssetbundlebaseLoadOperation
    {
        protected string _assetbundleName;
        protected string _levelName;
        protected bool _isAdditive;
        protected string _error;
        protected AsyncOperation _operation;

        public AssetBundleLoadLevelOperation(string assetbundleName, string levelName, bool isAdditive = false)
        {
            _assetbundleName = assetbundleName;
            _levelName = levelName;
            _isAdditive = isAdditive;
        }

        public override bool IsDone()
        {
            if (null == _operation && null != _error)
            {
                Debug.LogError(_error);
                return true;
            }
            return null != _operation && _operation.isDone;
        }

        public override bool Update()
        {
            if (null != _operation)
            {
                return false;
            }

            LoadedAssetbundle loadedAssetbundle = AssetbundleManager.GetLoadedAssetbundle(_assetbundleName, out _error);

            if (null != loadedAssetbundle)
            {
                _operation = SceneManager.LoadSceneAsync(_levelName, _isAdditive ? LoadSceneMode.Additive : LoadSceneMode.Single);
                return false;
            }
            else
            {
                return true;
            }
        }
    }

    #endregion


    #region LoadAsset

    public abstract class AssetbundleLoadAssetOperation : AssetbundlebaseLoadOperation
    {
        public abstract T GetAsset<T>() where T : UnityEngine.Object;
    }

    public class AssetbundleLoadAssetSimulationOperation : AssetbundleLoadAssetOperation
    {
        private UnityEngine.Object _simulatedObject;

        public AssetbundleLoadAssetSimulationOperation(UnityEngine.Object simulatedObject)
        {
            _simulatedObject = simulatedObject;
        }

        public override T GetAsset<T>()
        {
            return (T)_simulatedObject;
        }

        public override bool IsDone()
        {
            return false;
        }

        public override bool Update()
        {
            return true;
        }
    }

    public class AssetbundleLoadAssetFullOperation : AssetbundleLoadAssetOperation
    {
        protected string _bundleName;
        protected string _assetName;
        protected string _error;
        protected Type _type;
        protected AssetBundleRequest _request;

        public AssetbundleLoadAssetFullOperation(string assetbundleName,string assetName,Type type)
        {
            _bundleName = assetbundleName;
            _assetName = assetName;
            _type = type;
        }

        public override T GetAsset<T>()
        {
            if(null!=_request && _request.isDone)
            {
                return (T)_request.asset;
            }
            else
            {
                return null;
            }
        }

        public override bool IsDone()
        {
            if(null==_request && null!=_error)
            {
                Debug.LogError(_error);
                return true;
            }
            return null != _request && _request.isDone;
        }

        public override bool Update()
        {
            if(null!=_request)
            {
                return false;
            }

            LoadedAssetbundle loadedAssetbundle = AssetbundleManager.GetLoadedAssetbundle(_bundleName, out _error);
            if(null!=loadedAssetbundle)
            {
                _request = loadedAssetbundle._assetbundle.LoadAssetAsync(_assetName, _type);
                return false;
            }
            else
            {
                return true;
            }
        }
    }

    public class AssetbundleLoadManifestOperaton : AssetbundleLoadAssetFullOperation
    {
        public AssetbundleLoadManifestOperaton(string assetbundleName, string assetName, Type type) 
            : base(assetbundleName, assetName, type)
        {
        }

        public override bool Update()
        {
            base.Update();

            if(null!=_request && _request.isDone)
            {
                AssetbundleManager.AssetBundleManifest = GetAsset<AssetBundleManifest>();
                return false;
            }
            else
            {
                return true;
            }
        }
    }

    #endregion
}
